// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/views/controls/textfield/textfield.h"

#include <stddef.h>
#include <stdint.h>

#include <set>
#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/i18n/rtl.h"
#include "base/macros.h"
#include "base/pickle.h"
#include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "ui/accessibility/ax_view_state.h"
#include "ui/base/clipboard/clipboard.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/dragdrop/drag_drop_types.h"
#include "ui/base/ime/input_method_base.h"
#include "ui/base/ime/input_method_delegate.h"
#include "ui/base/ime/input_method_factory.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/base/ui_base_switches.h"
#include "ui/base/ui_base_switches_util.h"
#include "ui/events/event.h"
#include "ui/events/event_processor.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_codes.h"
#include "ui/events/test/event_generator.h"
#include "ui/gfx/render_text.h"
#include "ui/strings/grit/ui_strings.h"
#include "ui/views/controls/textfield/textfield_controller.h"
#include "ui/views/controls/textfield/textfield_model.h"
#include "ui/views/controls/textfield/textfield_test_api.h"
#include "ui/views/focus/focus_manager.h"
#include "ui/views/test/test_views_delegate.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/widget/widget.h"
#include "url/gurl.h"

#if defined(OS_WIN)
#include "base/win/windows_version.h"
#endif

#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
#include "ui/events/linux/text_edit_key_bindings_delegate_auralinux.h"
#endif

#if defined(USE_X11)
#include "ui/events/event_utils.h"
#endif

using base::ASCIIToUTF16;
using base::UTF8ToUTF16;
using base::WideToUTF16;

#define EXPECT_STR_EQ(ascii, utf16) EXPECT_EQ(ASCIIToUTF16(ascii), utf16)

namespace {

const base::char16 kHebrewLetterSamekh = 0x05E1;

class MockInputMethod : public ui::InputMethodBase {
public:
    MockInputMethod();
    ~MockInputMethod() override;

    // Overridden from InputMethod:
    bool OnUntranslatedIMEMessage(const base::NativeEvent& event,
        NativeEventResult* result) override;
    void DispatchKeyEvent(ui::KeyEvent* key) override;
    void OnTextInputTypeChanged(const ui::TextInputClient* client) override;
    void OnCaretBoundsChanged(const ui::TextInputClient* client) override { }
    void CancelComposition(const ui::TextInputClient* client) override;
    void OnInputLocaleChanged() override { }
    std::string GetInputLocale() override;
    bool IsCandidatePopupOpen() const override;
    void ShowImeIfNeeded() override { }

    bool untranslated_ime_message_called() const
    {
        return untranslated_ime_message_called_;
    }
    bool text_input_type_changed() const { return text_input_type_changed_; }
    bool cancel_composition_called() const { return cancel_composition_called_; }

    // Clears all internal states and result.
    void Clear();

    void SetCompositionTextForNextKey(const ui::CompositionText& composition);
    void SetResultTextForNextKey(const base::string16& result);

private:
    // Overridden from InputMethodBase.
    void OnWillChangeFocusedClient(ui::TextInputClient* focused_before,
        ui::TextInputClient* focused) override;

    // Clears boolean states defined below.
    void ClearStates();

    // Whether a mock composition or result is scheduled for the next key event.
    bool HasComposition();

    // Clears only composition information and result text.
    void ClearComposition();

    // Composition information for the next key event. It'll be cleared
    // automatically after dispatching the next key event.
    ui::CompositionText composition_;

    // Result text for the next key event. It'll be cleared automatically after
    // dispatching the next key event.
    base::string16 result_text_;

    // Record call state of corresponding methods. They will be set to false
    // automatically before dispatching a key event.
    bool untranslated_ime_message_called_;
    bool text_input_type_changed_;
    bool cancel_composition_called_;

    DISALLOW_COPY_AND_ASSIGN(MockInputMethod);
};

MockInputMethod::MockInputMethod()
    : untranslated_ime_message_called_(false)
    , text_input_type_changed_(false)
    , cancel_composition_called_(false)
{
}

MockInputMethod::~MockInputMethod()
{
}

bool MockInputMethod::OnUntranslatedIMEMessage(const base::NativeEvent& event,
    NativeEventResult* result)
{
    if (result)
        *result = NativeEventResult();
    return false;
}

void MockInputMethod::DispatchKeyEvent(ui::KeyEvent* key)
{
    // Checks whether the key event is from EventGenerator on Windows which will
    // generate key event for WM_CHAR.
    // The MockInputMethod will insert char on WM_KEYDOWN so ignore WM_CHAR here.
    if (key->is_char() && key->HasNativeEvent()) {
        key->SetHandled();
        return;
    }

    bool handled = !IsTextInputTypeNone() && HasComposition();
    ClearStates();
    if (handled) {
        DCHECK(!key->is_char());
        ui::KeyEvent mock_key(ui::ET_KEY_PRESSED,
            ui::VKEY_PROCESSKEY,
            key->flags());
        DispatchKeyEventPostIME(&mock_key);
    } else {
        DispatchKeyEventPostIME(key);
    }

    ui::TextInputClient* client = GetTextInputClient();
    if (client) {
        if (handled) {
            if (result_text_.length())
                client->InsertText(result_text_);
            if (composition_.text.length())
                client->SetCompositionText(composition_);
            else
                client->ClearCompositionText();
        } else if (key->type() == ui::ET_KEY_PRESSED) {
            base::char16 ch = key->GetCharacter();
            if (ch)
                client->InsertChar(*key);
        }
    }

    ClearComposition();
}

void MockInputMethod::OnTextInputTypeChanged(
    const ui::TextInputClient* client)
{
    if (IsTextInputClientFocused(client))
        text_input_type_changed_ = true;
    InputMethodBase::OnTextInputTypeChanged(client);
}

void MockInputMethod::CancelComposition(const ui::TextInputClient* client)
{
    if (IsTextInputClientFocused(client)) {
        cancel_composition_called_ = true;
        ClearComposition();
    }
}

std::string MockInputMethod::GetInputLocale()
{
    return "en-US";
}

bool MockInputMethod::IsCandidatePopupOpen() const
{
    return false;
}

void MockInputMethod::OnWillChangeFocusedClient(
    ui::TextInputClient* focused_before,
    ui::TextInputClient* focused)
{
    ui::TextInputClient* client = GetTextInputClient();
    if (client && client->HasCompositionText())
        client->ConfirmCompositionText();
    ClearComposition();
}

void MockInputMethod::Clear()
{
    ClearStates();
    ClearComposition();
}

void MockInputMethod::SetCompositionTextForNextKey(
    const ui::CompositionText& composition)
{
    composition_ = composition;
}

void MockInputMethod::SetResultTextForNextKey(const base::string16& result)
{
    result_text_ = result;
}

void MockInputMethod::ClearStates()
{
    untranslated_ime_message_called_ = false;
    text_input_type_changed_ = false;
    cancel_composition_called_ = false;
}

bool MockInputMethod::HasComposition()
{
    return composition_.text.length() || result_text_.length();
}

void MockInputMethod::ClearComposition()
{
    composition_.Clear();
    result_text_.clear();
}

// A Textfield wrapper to intercept OnKey[Pressed|Released]() ressults.
class TestTextfield : public views::Textfield {
public:
    TestTextfield()
        : Textfield()
        , key_handled_(false)
        , key_received_(false)
        , weak_ptr_factory_(this)
    {
    }

    bool OnKeyPressed(const ui::KeyEvent& e) override
    {
        key_received_ = true;

        // Since OnKeyPressed() might destroy |this|, get a weak pointer and
        // verify it isn't null before writing the bool value to key_handled_.
        base::WeakPtr<TestTextfield> textfield(weak_ptr_factory_.GetWeakPtr());
        bool key = views::Textfield::OnKeyPressed(e);

        if (!textfield)
            return key;

        key_handled_ = key;

        return key_handled_;
    }

    bool OnKeyReleased(const ui::KeyEvent& e) override
    {
        key_received_ = true;
        key_handled_ = views::Textfield::OnKeyReleased(e);
        EXPECT_FALSE(key_handled_); // Textfield doesn't override OnKeyReleased.
        return key_handled_;
    }

    // ui::TextInputClient overrides:
    void InsertChar(const ui::KeyEvent& e) override
    {
        views::Textfield::InsertChar(e);
#if defined(OS_MACOSX)
        // On Mac, characters are inserted directly rather than attempting to get a
        // unicode character from the ui::KeyEvent (which isn't always possible).
        key_received_ = true;
#endif
    }

    bool key_handled() const { return key_handled_; }
    bool key_received() const { return key_received_; }

    void clear() { key_received_ = key_handled_ = false; }

private:
    bool key_handled_;
    bool key_received_;

    base::WeakPtrFactory<TestTextfield> weak_ptr_factory_;

    DISALLOW_COPY_AND_ASSIGN(TestTextfield);
};

// Convenience to make constructing a GestureEvent simpler.
class GestureEventForTest : public ui::GestureEvent {
public:
    GestureEventForTest(int x, int y, ui::GestureEventDetails details)
        : GestureEvent(x, y, 0, base::TimeDelta(), details)
    {
    }

private:
    DISALLOW_COPY_AND_ASSIGN(GestureEventForTest);
};

// This controller will happily destroy the target textfield passed on
// construction when a key event is triggered.
class TextfieldDestroyerController : public views::TextfieldController {
public:
    explicit TextfieldDestroyerController(views::Textfield* target)
        : target_(target)
    {
        target_->set_controller(this);
    }

    views::Textfield* target() { return target_.get(); }

    // views::TextfieldController:
    bool HandleKeyEvent(views::Textfield* sender,
        const ui::KeyEvent& key_event) override
    {
        target_->OnBlur();
        target_.reset();
        return false;
    }

private:
    scoped_ptr<views::Textfield> target_;
};

base::string16 GetClipboardText(ui::ClipboardType type)
{
    base::string16 text;
    ui::Clipboard::GetForCurrentThread()->ReadText(type, &text);
    return text;
}

void SetClipboardText(ui::ClipboardType type, const std::string& text)
{
    ui::ScopedClipboardWriter(type).WriteText(ASCIIToUTF16(text));
}

} // namespace

namespace views {

class TextfieldTest : public ViewsTestBase, public TextfieldController {
public:
    TextfieldTest()
        : widget_(NULL)
        , textfield_(NULL)
        , model_(NULL)
        , input_method_(NULL)
        , on_before_user_action_(0)
        , on_after_user_action_(0)
        , copied_to_clipboard_(ui::CLIPBOARD_TYPE_LAST)
    {
        input_method_ = new MockInputMethod();
        ui::SetUpInputMethodForTesting(input_method_);
    }

    // ::testing::Test:
    void TearDown() override
    {
        if (widget_)
            widget_->Close();
        ViewsTestBase::TearDown();
    }

    ui::ClipboardType GetAndResetCopiedToClipboard()
    {
        ui::ClipboardType clipboard_type = copied_to_clipboard_;
        copied_to_clipboard_ = ui::CLIPBOARD_TYPE_LAST;
        return clipboard_type;
    }

    // TextfieldController:
    void ContentsChanged(Textfield* sender,
        const base::string16& new_contents) override
    {
        // Paste calls TextfieldController::ContentsChanged() explicitly even if the
        // paste action did not change the content. So |new_contents| may match
        // |last_contents_|. For more info, see http://crbug.com/79002
        last_contents_ = new_contents;
    }

    void OnBeforeUserAction(Textfield* sender) override
    {
        ++on_before_user_action_;
    }

    void OnAfterUserAction(Textfield* sender) override
    {
        ++on_after_user_action_;
    }

    void OnAfterCutOrCopy(ui::ClipboardType clipboard_type) override
    {
        copied_to_clipboard_ = clipboard_type;
    }

    void InitTextfield()
    {
        InitTextfields(1);
    }

    void InitTextfields(int count)
    {
        ASSERT_FALSE(textfield_);
        textfield_ = new TestTextfield();
        textfield_->set_controller(this);
        widget_ = new Widget();

        // The widget type must be an activatable type, and we don't want to worry
        // about the non-client view, which leaves just TYPE_WINDOW_FRAMELESS.
        Widget::InitParams params = CreateParams(Widget::InitParams::TYPE_WINDOW_FRAMELESS);

        params.bounds = gfx::Rect(100, 100, 100, 100);
        widget_->Init(params);
        input_method_->SetDelegate(
            test::WidgetTest::GetInputMethodDelegateForWidget(widget_));
        View* container = new View();
        widget_->SetContentsView(container);
        container->AddChildView(textfield_);
        textfield_->SetBoundsRect(params.bounds);
        textfield_->set_id(1);
        test_api_.reset(new TextfieldTestApi(textfield_));

        for (int i = 1; i < count; i++) {
            Textfield* textfield = new Textfield();
            container->AddChildView(textfield);
            textfield->set_id(i + 1);
        }

        model_ = test_api_->model();
        model_->ClearEditHistory();

        // Since the window type is activatable, showing the widget will also
        // activate it. Calling Activate directly is insufficient, since that does
        // not also _focus_ an aura::Window (i.e. using the FocusClient). Both the
        // widget and the textfield must have focus to properly handle input.
        widget_->Show();
        textfield_->RequestFocus();

        // On Mac, activation is asynchronous since desktop widgets are used. We
        // don't want parallel tests to steal active status either, so fake it.
#if defined(OS_MACOSX) && !defined(USE_AURA)
        fake_activation_ = test::WidgetTest::FakeWidgetIsActiveAlways();
#endif
        event_generator_.reset(
            new ui::test::EventGenerator(GetContext(), widget_->GetNativeWindow()));
    }
    ui::MenuModel* GetContextMenuModel()
    {
        test_api_->UpdateContextMenu();
        return test_api_->context_menu_contents();
    }

    // True if native Mac keystrokes should be used (to avoid ifdef litter).
    bool TestingNativeMac()
    {
#if defined(OS_MACOSX)
        return true;
#else
        return false;
#endif
    }

    bool TestingNativeCrOs() const
    {
#if defined(OS_CHROMEOS)
        return true;
#else
        return false;
#endif // defined(OS_CHROMEOS)
    }

protected:
    void SendKeyPress(ui::KeyboardCode key_code, int flags)
    {
        event_generator_->PressKey(key_code, flags);
    }

    void SendKeyEvent(ui::KeyboardCode key_code,
        bool alt,
        bool shift,
        bool control_or_command,
        bool caps_lock)
    {
        bool control = control_or_command;
        bool command = false;

        // By default, swap control and command for native events on Mac. This
        // handles most cases.
        if (TestingNativeMac())
            std::swap(control, command);

        int flags = (shift ? ui::EF_SHIFT_DOWN : 0) | (control ? ui::EF_CONTROL_DOWN : 0) | (alt ? ui::EF_ALT_DOWN : 0) | (command ? ui::EF_COMMAND_DOWN : 0) | (caps_lock ? ui::EF_CAPS_LOCK_ON : 0);

        SendKeyPress(key_code, flags);
    }

    void SendKeyEvent(ui::KeyboardCode key_code,
        bool shift,
        bool control_or_command)
    {
        SendKeyEvent(key_code, false, shift, control_or_command, false);
    }

    void SendKeyEvent(ui::KeyboardCode key_code)
    {
        SendKeyEvent(key_code, false, false);
    }

    void SendKeyEvent(base::char16 ch)
    {
        if (ch < 0x80) {
            ui::KeyboardCode code = ch == ' ' ? ui::VKEY_SPACE : static_cast<ui::KeyboardCode>(ui::VKEY_A + ch - 'a');
            SendKeyEvent(code);
        } else {
            // For unicode characters, assume they come from IME rather than the
            // keyboard. So they are dispatched directly to the input method.
            ui::KeyEvent event(ch, ui::VKEY_UNKNOWN, ui::EF_NONE);
            input_method_->DispatchKeyEvent(&event);
        }
    }

    // Sends a platform-specific move (and select) to start of line.
    void SendHomeEvent(bool shift)
    {
        if (TestingNativeMac()) {
            // Use Cmd+Left on native Mac. An RTL-agnostic "end" doesn't have a
            // default key-binding on Mac.
            SendKeyEvent(ui::VKEY_LEFT, shift /* shift */, true /* command */);
            return;
        }
        SendKeyEvent(ui::VKEY_HOME, shift /* shift */, false /* control */);
    }

    // Sends a platform-specific move (and select) to end of line.
    void SendEndEvent(bool shift)
    {
        if (TestingNativeMac()) {
            SendKeyEvent(ui::VKEY_RIGHT, shift, true); // Cmd+Right.
            return;
        }
        SendKeyEvent(ui::VKEY_END, shift, false);
    }

    // Sends {delete, move, select} word {forward, backward}.
    void SendWordEvent(ui::KeyboardCode key, bool shift)
    {
        bool alt = false;
        bool control = true;
        bool caps = false;
        if (TestingNativeMac()) {
            // Use Alt+Left/Right/Backspace on native Mac.
            alt = true;
            control = false;
        }
        SendKeyEvent(key, alt, shift, control, caps);
    }

    // Sends Shift+Delete if supported, otherwise Cmd+X again.
    void SendAlternateCut()
    {
        if (TestingNativeMac())
            SendKeyEvent(ui::VKEY_X, false, true);
        else
            SendKeyEvent(ui::VKEY_DELETE, true, false);
    }

    // Sends Ctrl+Insert if supported, otherwise Cmd+C again.
    void SendAlternateCopy()
    {
        if (TestingNativeMac())
            SendKeyEvent(ui::VKEY_C, false, true);
        else
            SendKeyEvent(ui::VKEY_INSERT, false, true);
    }

    // Sends Shift+Insert if supported, otherwise Cmd+V again.
    void SendAlternatePaste()
    {
        if (TestingNativeMac())
            SendKeyEvent(ui::VKEY_V, false, true);
        else
            SendKeyEvent(ui::VKEY_INSERT, true, false);
    }

    View* GetFocusedView()
    {
        return widget_->GetFocusManager()->GetFocusedView();
    }

    int GetCursorPositionX(int cursor_pos)
    {
        return test_api_->GetRenderText()->GetCursorBounds(
                                             gfx::SelectionModel(cursor_pos, gfx::CURSOR_FORWARD), false)
            .x();
    }

    // Get the current cursor bounds.
    gfx::Rect GetCursorBounds()
    {
        return test_api_->GetRenderText()->GetUpdatedCursorBounds();
    }

    // Get the cursor bounds of |sel|.
    gfx::Rect GetCursorBounds(const gfx::SelectionModel& sel)
    {
        return test_api_->GetRenderText()->GetCursorBounds(sel, true);
    }

    gfx::Rect GetDisplayRect()
    {
        return test_api_->GetRenderText()->display_rect();
    }

    // Mouse click on the point whose x-axis is |bound|'s x plus |x_offset| and
    // y-axis is in the middle of |bound|'s vertical range.
    void MouseClick(const gfx::Rect bound, int x_offset)
    {
        gfx::Point point(bound.x() + x_offset, bound.y() + bound.height() / 2);
        ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point,
            ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
            ui::EF_LEFT_MOUSE_BUTTON);
        textfield_->OnMousePressed(click);
        ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point, point,
            ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
            ui::EF_LEFT_MOUSE_BUTTON);
        textfield_->OnMouseReleased(release);
    }

    // This is to avoid double/triple click.
    void NonClientMouseClick()
    {
        ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
            ui::EventTimeForNow(),
            ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
            ui::EF_LEFT_MOUSE_BUTTON);
        textfield_->OnMousePressed(click);
        ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
            ui::EventTimeForNow(),
            ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_NON_CLIENT,
            ui::EF_LEFT_MOUSE_BUTTON);
        textfield_->OnMouseReleased(release);
    }

    void VerifyTextfieldContextMenuContents(bool textfield_has_selection,
        bool can_undo,
        ui::MenuModel* menu)
    {
        EXPECT_EQ(can_undo, menu->IsEnabledAt(0 /* UNDO */));
        EXPECT_TRUE(menu->IsEnabledAt(1 /* Separator */));
        EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(2 /* CUT */));
        EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(3 /* COPY */));
        EXPECT_NE(GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE).empty(),
            menu->IsEnabledAt(4 /* PASTE */));
        EXPECT_EQ(textfield_has_selection, menu->IsEnabledAt(5 /* DELETE */));
        EXPECT_TRUE(menu->IsEnabledAt(6 /* Separator */));
        EXPECT_TRUE(menu->IsEnabledAt(7 /* SELECT ALL */));
    }

    // We need widget to populate wrapper class.
    Widget* widget_;

    TestTextfield* textfield_;
    scoped_ptr<TextfieldTestApi> test_api_;
    TextfieldModel* model_;

    // The string from Controller::ContentsChanged callback.
    base::string16 last_contents_;

    // For testing input method related behaviors.
    MockInputMethod* input_method_;

    // Indicates how many times OnBeforeUserAction() is called.
    int on_before_user_action_;

    // Indicates how many times OnAfterUserAction() is called.
    int on_after_user_action_;

private:
    ui::ClipboardType copied_to_clipboard_;
    scoped_ptr<test::WidgetTest::FakeActivation> fake_activation_;
    scoped_ptr<ui::test::EventGenerator> event_generator_;
    DISALLOW_COPY_AND_ASSIGN(TextfieldTest);
};

TEST_F(TextfieldTest, ModelChangesTest)
{
    InitTextfield();

    // TextfieldController::ContentsChanged() shouldn't be called when changing
    // text programmatically.
    last_contents_.clear();
    textfield_->SetText(ASCIIToUTF16("this is"));

    EXPECT_STR_EQ("this is", model_->text());
    EXPECT_STR_EQ("this is", textfield_->text());
    EXPECT_TRUE(last_contents_.empty());

    textfield_->AppendText(ASCIIToUTF16(" a test"));
    EXPECT_STR_EQ("this is a test", model_->text());
    EXPECT_STR_EQ("this is a test", textfield_->text());
    EXPECT_TRUE(last_contents_.empty());

    EXPECT_EQ(base::string16(), textfield_->GetSelectedText());
    textfield_->SelectAll(false);
    EXPECT_STR_EQ("this is a test", textfield_->GetSelectedText());
    EXPECT_TRUE(last_contents_.empty());
}

TEST_F(TextfieldTest, KeyTest)
{
    InitTextfield();
    // Event flags:  key,    alt,   shift, ctrl,  caps-lock.
    SendKeyEvent(ui::VKEY_T, false, true, false, false);
    SendKeyEvent(ui::VKEY_E, false, false, false, false);
    SendKeyEvent(ui::VKEY_X, false, true, false, true);
    SendKeyEvent(ui::VKEY_T, false, false, false, true);
    SendKeyEvent(ui::VKEY_1, false, true, false, false);
    SendKeyEvent(ui::VKEY_1, false, false, false, false);
    SendKeyEvent(ui::VKEY_1, false, true, false, true);
    SendKeyEvent(ui::VKEY_1, false, false, false, true);

    // On Mac, Caps+Shift remains uppercase.
    if (TestingNativeMac())
        EXPECT_STR_EQ("TeXT!1!1", textfield_->text());
    else
        EXPECT_STR_EQ("TexT!1!1", textfield_->text());
}

TEST_F(TextfieldTest, KeysWithModifiersTest)
{
    InitTextfield();
    const int ctrl = ui::EF_CONTROL_DOWN;
    const int alt = ui::EF_ALT_DOWN;
    const int command = ui::EF_COMMAND_DOWN;
    const int altgr = ui::EF_ALTGR_DOWN;
    const int shift = ui::EF_SHIFT_DOWN;

    SendKeyPress(ui::VKEY_T, shift | alt | altgr);
    SendKeyPress(ui::VKEY_H, alt);
    SendKeyPress(ui::VKEY_E, altgr);
    SendKeyPress(ui::VKEY_T, shift);
    SendKeyPress(ui::VKEY_E, shift | altgr);
    SendKeyPress(ui::VKEY_X, 0);
    SendKeyPress(ui::VKEY_T, ctrl);
    SendKeyPress(ui::VKEY_1, alt);
    SendKeyPress(ui::VKEY_2, command);
    SendKeyPress(ui::VKEY_3, 0);
    SendKeyPress(ui::VKEY_4, 0);
    SendKeyPress(ui::VKEY_OEM_PLUS, ctrl);
    SendKeyPress(ui::VKEY_OEM_PLUS, ctrl | shift);
    SendKeyPress(ui::VKEY_OEM_MINUS, ctrl);
    SendKeyPress(ui::VKEY_OEM_MINUS, ctrl | shift);

    if (TestingNativeCrOs())
        EXPECT_STR_EQ("TeTEx34", textfield_->text());
    else if (TestingNativeMac())
        EXPECT_STR_EQ("TheTEx134", textfield_->text());
    else
        EXPECT_STR_EQ("TeTEx234", textfield_->text());
}

TEST_F(TextfieldTest, ControlAndSelectTest)
{
    // Insert a test string in a textfield.
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("one two three"));
    SendHomeEvent(false);
    SendKeyEvent(ui::VKEY_RIGHT, true, false);
    SendKeyEvent(ui::VKEY_RIGHT, true, false);
    SendKeyEvent(ui::VKEY_RIGHT, true, false);

    EXPECT_STR_EQ("one", textfield_->GetSelectedText());

    // Test word select.
    SendWordEvent(ui::VKEY_RIGHT, true);
    EXPECT_STR_EQ("one two", textfield_->GetSelectedText());
    SendWordEvent(ui::VKEY_RIGHT, true);
    EXPECT_STR_EQ("one two three", textfield_->GetSelectedText());
    SendWordEvent(ui::VKEY_LEFT, true);
    EXPECT_STR_EQ("one two ", textfield_->GetSelectedText());
    SendWordEvent(ui::VKEY_LEFT, true);
    EXPECT_STR_EQ("one ", textfield_->GetSelectedText());

    // Replace the selected text.
    SendKeyEvent(ui::VKEY_Z, true, false);
    SendKeyEvent(ui::VKEY_E, true, false);
    SendKeyEvent(ui::VKEY_R, true, false);
    SendKeyEvent(ui::VKEY_O, true, false);
    SendKeyEvent(ui::VKEY_SPACE, false, false);
    EXPECT_STR_EQ("ZERO two three", textfield_->text());

    SendEndEvent(true);
    EXPECT_STR_EQ("two three", textfield_->GetSelectedText());
    SendHomeEvent(true);
    EXPECT_STR_EQ("ZERO ", textfield_->GetSelectedText());
}

TEST_F(TextfieldTest, InsertionDeletionTest)
{
    // Insert a test string in a textfield.
    InitTextfield();
    for (size_t i = 0; i < 10; i++)
        SendKeyEvent(static_cast<ui::KeyboardCode>(ui::VKEY_A + i));
    EXPECT_STR_EQ("abcdefghij", textfield_->text());

    // Test the delete and backspace keys.
    textfield_->SelectRange(gfx::Range(5));
    for (int i = 0; i < 3; i++)
        SendKeyEvent(ui::VKEY_BACK);
    EXPECT_STR_EQ("abfghij", textfield_->text());
    for (int i = 0; i < 3; i++)
        SendKeyEvent(ui::VKEY_DELETE);
    EXPECT_STR_EQ("abij", textfield_->text());

    // Select all and replace with "k".
    textfield_->SelectAll(false);
    SendKeyEvent(ui::VKEY_K);
    EXPECT_STR_EQ("k", textfield_->text());

    // Delete the previous word from cursor.
    bool shift = false;
    textfield_->SetText(ASCIIToUTF16("one two three four"));
    SendEndEvent(shift);
    SendWordEvent(ui::VKEY_BACK, shift);
    EXPECT_STR_EQ("one two three ", textfield_->text());

    // Delete to a line break on Linux and ChromeOS, to a word break on Windows
    // and Mac.
    SendWordEvent(ui::VKEY_LEFT, shift);
    shift = true;
    SendWordEvent(ui::VKEY_BACK, shift);
#if defined(OS_LINUX)
    EXPECT_STR_EQ("three ", textfield_->text());
#else
    EXPECT_STR_EQ("one three ", textfield_->text());
#endif

    // Delete the next word from cursor.
    textfield_->SetText(ASCIIToUTF16("one two three four"));
    shift = false;
    SendHomeEvent(shift);
    SendWordEvent(ui::VKEY_DELETE, shift);
    EXPECT_STR_EQ(" two three four", textfield_->text());

    // Delete to a line break on Linux and ChromeOS, to a word break on Windows
    // and Mac.
    SendWordEvent(ui::VKEY_RIGHT, shift);
    shift = true;
    SendWordEvent(ui::VKEY_DELETE, shift);
#if defined(OS_LINUX)
    EXPECT_STR_EQ(" two", textfield_->text());
#else
    EXPECT_STR_EQ(" two four", textfield_->text());
#endif
}

TEST_F(TextfieldTest, PasswordTest)
{
    InitTextfield();
    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType());
    EXPECT_TRUE(textfield_->enabled());
    EXPECT_TRUE(textfield_->IsFocusable());

    last_contents_.clear();
    textfield_->SetText(ASCIIToUTF16("password"));
    // Ensure text() and the callback returns the actual text instead of "*".
    EXPECT_STR_EQ("password", textfield_->text());
    EXPECT_TRUE(last_contents_.empty());
    model_->SelectAll(false);
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "foo");

    // Cut and copy should be disabled.
    EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT));
    textfield_->ExecuteCommand(IDS_APP_CUT, 0);
    SendKeyEvent(ui::VKEY_X, false, true);
    EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_COPY));
    textfield_->ExecuteCommand(IDS_APP_COPY, 0);
    SendKeyEvent(ui::VKEY_C, false, true);
    SendAlternateCopy();
    EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("password", textfield_->text());
    // [Shift]+[Delete] should just delete without copying text to the clipboard.
    textfield_->SelectAll(false);
    SendKeyEvent(ui::VKEY_DELETE, true, false);

    // Paste should work normally.
    EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE));
    textfield_->ExecuteCommand(IDS_APP_PASTE, 0);
    SendKeyEvent(ui::VKEY_V, false, true);
    SendAlternatePaste();
    EXPECT_STR_EQ("foo", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("foofoofoo", textfield_->text());
}

TEST_F(TextfieldTest, TextInputType)
{
    InitTextfield();

    // Defaults to TEXT
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, textfield_->GetTextInputType());

    // And can be set.
    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_URL);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_URL, textfield_->GetTextInputType());
    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType());

    // Readonly textfields have type NONE
    textfield_->SetReadOnly(true);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, textfield_->GetTextInputType());

    textfield_->SetReadOnly(false);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType());

    // As do disabled textfields
    textfield_->SetEnabled(false);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, textfield_->GetTextInputType());

    textfield_->SetEnabled(true);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_PASSWORD, textfield_->GetTextInputType());
}

TEST_F(TextfieldTest, OnKeyPress)
{
    InitTextfield();

    // Character keys are handled by the input method.
    SendKeyEvent(ui::VKEY_A);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_FALSE(textfield_->key_handled());
    textfield_->clear();

    // Arrow keys and home/end are handled by the textfield.
    SendKeyEvent(ui::VKEY_LEFT);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    textfield_->clear();

    SendKeyEvent(ui::VKEY_RIGHT);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    textfield_->clear();

    const bool shift = false;
    SendHomeEvent(shift);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    textfield_->clear();

    SendEndEvent(shift);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    textfield_->clear();

    // F20, up/down key won't be handled.
    SendKeyEvent(ui::VKEY_F20);
#if defined(OS_MACOSX)
    // On Mac, key combinations that don't map to editing commands are forwarded
    // on to the next responder, usually ending up at the window, which will beep.
    EXPECT_FALSE(textfield_->key_received());
#else
    EXPECT_TRUE(textfield_->key_received());
#endif
    EXPECT_FALSE(textfield_->key_handled());
    textfield_->clear();

    SendKeyEvent(ui::VKEY_UP);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_FALSE(textfield_->key_handled());
    textfield_->clear();

    SendKeyEvent(ui::VKEY_DOWN);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_FALSE(textfield_->key_handled());
    textfield_->clear();
}

// Tests that default key bindings are handled even with a delegate installed.
TEST_F(TextfieldTest, OnKeyPressBinding)
{
    InitTextfield();

#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
    // Install a TextEditKeyBindingsDelegateAuraLinux that does nothing.
    class TestDelegate : public ui::TextEditKeyBindingsDelegateAuraLinux {
    public:
        TestDelegate() { }
        ~TestDelegate() override { }

        bool MatchEvent(
            const ui::Event& event,
            std::vector<ui::TextEditCommandAuraLinux>* commands) override
        {
            return false;
        }

    private:
        DISALLOW_COPY_AND_ASSIGN(TestDelegate);
    };

    TestDelegate delegate;
    ui::SetTextEditKeyBindingsDelegate(&delegate);
#endif

    SendKeyEvent(ui::VKEY_A, false, false);
    EXPECT_STR_EQ("a", textfield_->text());
    textfield_->clear();

    // Undo/Redo command keys are handled by the textfield.
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    EXPECT_TRUE(textfield_->text().empty());
    textfield_->clear();

    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    EXPECT_STR_EQ("a", textfield_->text());
    textfield_->clear();

#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
    ui::SetTextEditKeyBindingsDelegate(NULL);
#endif
}

TEST_F(TextfieldTest, CursorMovement)
{
    InitTextfield();

    // Test with trailing whitespace.
    textfield_->SetText(ASCIIToUTF16("one two hre "));

    // Send the cursor at the end.
    SendKeyEvent(ui::VKEY_END);

    // Ctrl+Left should move the cursor just before the last word.
    const bool shift = false;
    SendWordEvent(ui::VKEY_LEFT, shift);
    SendKeyEvent(ui::VKEY_T);
    EXPECT_STR_EQ("one two thre ", textfield_->text());
    EXPECT_STR_EQ("one two thre ", last_contents_);

    // Ctrl+Right should move the cursor to the end of the last word.
    SendWordEvent(ui::VKEY_RIGHT, shift);
    SendKeyEvent(ui::VKEY_E);
    EXPECT_STR_EQ("one two three ", textfield_->text());
    EXPECT_STR_EQ("one two three ", last_contents_);

    // Ctrl+Right again should move the cursor to the end.
    SendWordEvent(ui::VKEY_RIGHT, shift);
    SendKeyEvent(ui::VKEY_BACK);
    EXPECT_STR_EQ("one two three", textfield_->text());
    EXPECT_STR_EQ("one two three", last_contents_);

    // Test with leading whitespace.
    textfield_->SetText(ASCIIToUTF16(" ne two"));

    // Send the cursor at the beginning.
    SendHomeEvent(shift);

    // Ctrl+Right, then Ctrl+Left should move the cursor to the beginning of the
    // first word.
    SendWordEvent(ui::VKEY_RIGHT, shift);
    SendWordEvent(ui::VKEY_LEFT, shift);
    SendKeyEvent(ui::VKEY_O);
    EXPECT_STR_EQ(" one two", textfield_->text());
    EXPECT_STR_EQ(" one two", last_contents_);

    // Ctrl+Left to move the cursor to the beginning of the first word.
    SendWordEvent(ui::VKEY_LEFT, shift);
    // Ctrl+Left again should move the cursor back to the very beginning.
    SendWordEvent(ui::VKEY_LEFT, shift);
    SendKeyEvent(ui::VKEY_DELETE);
    EXPECT_STR_EQ("one two", textfield_->text());
    EXPECT_STR_EQ("one two", last_contents_);
}

TEST_F(TextfieldTest, FocusTraversalTest)
{
    InitTextfields(3);
    textfield_->RequestFocus();

    EXPECT_EQ(1, GetFocusedView()->id());
    widget_->GetFocusManager()->AdvanceFocus(false);
    EXPECT_EQ(2, GetFocusedView()->id());
    widget_->GetFocusManager()->AdvanceFocus(false);
    EXPECT_EQ(3, GetFocusedView()->id());
    // Cycle back to the first textfield.
    widget_->GetFocusManager()->AdvanceFocus(false);
    EXPECT_EQ(1, GetFocusedView()->id());

    widget_->GetFocusManager()->AdvanceFocus(true);
    EXPECT_EQ(3, GetFocusedView()->id());
    widget_->GetFocusManager()->AdvanceFocus(true);
    EXPECT_EQ(2, GetFocusedView()->id());
    widget_->GetFocusManager()->AdvanceFocus(true);
    EXPECT_EQ(1, GetFocusedView()->id());
    // Cycle back to the last textfield.
    widget_->GetFocusManager()->AdvanceFocus(true);
    EXPECT_EQ(3, GetFocusedView()->id());

    // Request focus should still work.
    textfield_->RequestFocus();
    EXPECT_EQ(1, GetFocusedView()->id());

    // Test if clicking on textfield view sets the focus.
    widget_->GetFocusManager()->AdvanceFocus(true);
    EXPECT_EQ(3, GetFocusedView()->id());
    ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(click);
    EXPECT_EQ(1, GetFocusedView()->id());

    // Tab/Shift+Tab should also cycle focus, not insert a tab character.
    SendKeyEvent(ui::VKEY_TAB, false, false);
    EXPECT_EQ(2, GetFocusedView()->id());
    SendKeyEvent(ui::VKEY_TAB, false, false);
    EXPECT_EQ(3, GetFocusedView()->id());
    // Cycle back to the first textfield.
    SendKeyEvent(ui::VKEY_TAB, false, false);
    EXPECT_EQ(1, GetFocusedView()->id());

    SendKeyEvent(ui::VKEY_TAB, true, false);
    EXPECT_EQ(3, GetFocusedView()->id());
    SendKeyEvent(ui::VKEY_TAB, true, false);
    EXPECT_EQ(2, GetFocusedView()->id());
    SendKeyEvent(ui::VKEY_TAB, true, false);
    EXPECT_EQ(1, GetFocusedView()->id());
    // Cycle back to the last textfield.
    SendKeyEvent(ui::VKEY_TAB, true, false);
    EXPECT_EQ(3, GetFocusedView()->id());
}

TEST_F(TextfieldTest, ContextMenuDisplayTest)
{
    InitTextfield();
    EXPECT_TRUE(textfield_->context_menu_controller());
    textfield_->SetText(ASCIIToUTF16("hello world"));
    ui::Clipboard::GetForCurrentThread()->Clear(ui::CLIPBOARD_TYPE_COPY_PASTE);
    textfield_->ClearEditHistory();
    EXPECT_TRUE(GetContextMenuModel());
    VerifyTextfieldContextMenuContents(false, false, GetContextMenuModel());

    textfield_->SelectAll(false);
    VerifyTextfieldContextMenuContents(true, false, GetContextMenuModel());

    SendKeyEvent(ui::VKEY_T);
    VerifyTextfieldContextMenuContents(false, true, GetContextMenuModel());

    textfield_->SelectAll(false);
    VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel());

    // Exercise the "paste enabled?" check in the verifier.
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test");
    VerifyTextfieldContextMenuContents(true, true, GetContextMenuModel());
}

TEST_F(TextfieldTest, DoubleAndTripleClickTest)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));
    ui::MouseEvent click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, gfx::Point(), gfx::Point(),
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
        ui::EventTimeForNow(),
        ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK,
        ui::EF_LEFT_MOUSE_BUTTON);

    // Test for double click.
    textfield_->OnMousePressed(click);
    textfield_->OnMouseReleased(release);
    EXPECT_TRUE(textfield_->GetSelectedText().empty());
    textfield_->OnMousePressed(double_click);
    textfield_->OnMouseReleased(release);
    EXPECT_STR_EQ("hello", textfield_->GetSelectedText());

    // Test for triple click.
    textfield_->OnMousePressed(click);
    textfield_->OnMouseReleased(release);
    EXPECT_STR_EQ("hello world", textfield_->GetSelectedText());

    // Another click should reset back to double click.
    textfield_->OnMousePressed(click);
    textfield_->OnMouseReleased(release);
    EXPECT_STR_EQ("hello", textfield_->GetSelectedText());
}

TEST_F(TextfieldTest, DragToSelect)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));
    const int kStart = GetCursorPositionX(5);
    const int kEnd = 500;
    gfx::Point start_point(kStart, 0);
    gfx::Point end_point(kEnd, 0);
    ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, start_point, start_point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    ui::MouseEvent click_b(ui::ET_MOUSE_PRESSED, end_point, end_point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    ui::MouseEvent drag_left(ui::ET_MOUSE_DRAGGED, gfx::Point(), gfx::Point(),
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
    ui::MouseEvent drag_right(ui::ET_MOUSE_DRAGGED, end_point, end_point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, end_point, end_point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(click_a);
    EXPECT_TRUE(textfield_->GetSelectedText().empty());
    // Check that dragging left selects the beginning of the string.
    textfield_->OnMouseDragged(drag_left);
    base::string16 text_left = textfield_->GetSelectedText();
    EXPECT_STR_EQ("hello", text_left);
    // Check that dragging right selects the rest of the string.
    textfield_->OnMouseDragged(drag_right);
    base::string16 text_right = textfield_->GetSelectedText();
    EXPECT_STR_EQ(" world", text_right);
    // Check that releasing in the same location does not alter the selection.
    textfield_->OnMouseReleased(release);
    EXPECT_EQ(text_right, textfield_->GetSelectedText());
    // Check that dragging from beyond the text length works too.
    textfield_->OnMousePressed(click_b);
    textfield_->OnMouseDragged(drag_left);
    textfield_->OnMouseReleased(release);
    EXPECT_EQ(textfield_->text(), textfield_->GetSelectedText());
}

#if defined(OS_WIN)
TEST_F(TextfieldTest, DragAndDrop_AcceptDrop)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));

    ui::OSExchangeData data;
    base::string16 string(ASCIIToUTF16("string "));
    data.SetString(string);
    int formats = 0;
    std::set<ui::Clipboard::FormatType> format_types;

    // Ensure that disabled textfields do not accept drops.
    textfield_->SetEnabled(false);
    EXPECT_FALSE(textfield_->GetDropFormats(&formats, &format_types));
    EXPECT_EQ(0, formats);
    EXPECT_TRUE(format_types.empty());
    EXPECT_FALSE(textfield_->CanDrop(data));
    textfield_->SetEnabled(true);

    // Ensure that read-only textfields do not accept drops.
    textfield_->SetReadOnly(true);
    EXPECT_FALSE(textfield_->GetDropFormats(&formats, &format_types));
    EXPECT_EQ(0, formats);
    EXPECT_TRUE(format_types.empty());
    EXPECT_FALSE(textfield_->CanDrop(data));
    textfield_->SetReadOnly(false);

    // Ensure that enabled and editable textfields do accept drops.
    EXPECT_TRUE(textfield_->GetDropFormats(&formats, &format_types));
    EXPECT_EQ(ui::OSExchangeData::STRING, formats);
    EXPECT_TRUE(format_types.empty());
    EXPECT_TRUE(textfield_->CanDrop(data));
    gfx::Point drop_point(GetCursorPositionX(6), 0);
    ui::DropTargetEvent drop(data, drop_point, drop_point,
        ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE);
    EXPECT_EQ(ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_MOVE,
        textfield_->OnDragUpdated(drop));
    EXPECT_EQ(ui::DragDropTypes::DRAG_COPY, textfield_->OnPerformDrop(drop));
    EXPECT_STR_EQ("hello string world", textfield_->text());

    // Ensure that textfields do not accept non-OSExchangeData::STRING types.
    ui::OSExchangeData bad_data;
    bad_data.SetFilename(base::FilePath(FILE_PATH_LITERAL("x")));
    ui::Clipboard::FormatType fmt = ui::Clipboard::GetBitmapFormatType();
    bad_data.SetPickledData(fmt, base::Pickle());
    bad_data.SetFileContents(base::FilePath(L"x"), "x");
    bad_data.SetHtml(base::string16(ASCIIToUTF16("x")), GURL("x.org"));
    ui::OSExchangeData::DownloadFileInfo download(base::FilePath(), NULL);
    bad_data.SetDownloadFileInfo(download);
    EXPECT_FALSE(textfield_->CanDrop(bad_data));
}
#endif

TEST_F(TextfieldTest, DragAndDrop_InitiateDrag)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello string world"));

    // Ensure the textfield will provide selected text for drag data.
    base::string16 string;
    ui::OSExchangeData data;
    const gfx::Range kStringRange(6, 12);
    textfield_->SelectRange(kStringRange);
    const gfx::Point kStringPoint(GetCursorPositionX(9), 0);
    textfield_->WriteDragDataForView(NULL, kStringPoint, &data);
    EXPECT_TRUE(data.GetString(&string));
    EXPECT_EQ(textfield_->GetSelectedText(), string);

    // Ensure that disabled textfields do not support drag operations.
    textfield_->SetEnabled(false);
    EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
        textfield_->GetDragOperationsForView(NULL, kStringPoint));
    textfield_->SetEnabled(true);
    // Ensure that textfields without selections do not support drag operations.
    textfield_->ClearSelection();
    EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
        textfield_->GetDragOperationsForView(NULL, kStringPoint));
    textfield_->SelectRange(kStringRange);
    // Ensure that password textfields do not support drag operations.
    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
    EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
        textfield_->GetDragOperationsForView(NULL, kStringPoint));
    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_TEXT);
    // Ensure that textfields only initiate drag operations inside the selection.
    ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, kStringPoint, kStringPoint,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(press_event);
    EXPECT_EQ(ui::DragDropTypes::DRAG_NONE,
        textfield_->GetDragOperationsForView(NULL, gfx::Point()));
    EXPECT_FALSE(textfield_->CanStartDragForView(NULL, gfx::Point(),
        gfx::Point()));
    EXPECT_EQ(ui::DragDropTypes::DRAG_COPY,
        textfield_->GetDragOperationsForView(NULL, kStringPoint));
    EXPECT_TRUE(textfield_->CanStartDragForView(NULL, kStringPoint,
        gfx::Point()));
    // Ensure that textfields support local moves.
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY,
        textfield_->GetDragOperationsForView(textfield_, kStringPoint));
}

TEST_F(TextfieldTest, DragAndDrop_ToTheRight)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));

    base::string16 string;
    ui::OSExchangeData data;
    int formats = 0;
    int operations = 0;
    std::set<ui::Clipboard::FormatType> format_types;

    // Start dragging "ello".
    textfield_->SelectRange(gfx::Range(1, 5));
    gfx::Point point(GetCursorPositionX(3), 0);
    ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, point, point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(click_a);
    EXPECT_TRUE(textfield_->CanStartDragForView(textfield_, click_a.location(),
        gfx::Point()));
    operations = textfield_->GetDragOperationsForView(textfield_,
        click_a.location());
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY,
        operations);
    textfield_->WriteDragDataForView(NULL, click_a.location(), &data);
    EXPECT_TRUE(data.GetString(&string));
    EXPECT_EQ(textfield_->GetSelectedText(), string);
    EXPECT_TRUE(textfield_->GetDropFormats(&formats, &format_types));
    EXPECT_EQ(ui::OSExchangeData::STRING, formats);
    EXPECT_TRUE(format_types.empty());

    // Drop "ello" after "w".
    const gfx::Point kDropPoint(GetCursorPositionX(7), 0);
    EXPECT_TRUE(textfield_->CanDrop(data));
    ui::DropTargetEvent drop_a(data, kDropPoint, kDropPoint, operations);
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnDragUpdated(drop_a));
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnPerformDrop(drop_a));
    EXPECT_STR_EQ("h welloorld", textfield_->text());
    textfield_->OnDragDone();

    // Undo/Redo the drag&drop change.
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("hello world", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("hello world", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("h welloorld", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("h welloorld", textfield_->text());
}

TEST_F(TextfieldTest, DragAndDrop_ToTheLeft)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));

    base::string16 string;
    ui::OSExchangeData data;
    int formats = 0;
    int operations = 0;
    std::set<ui::Clipboard::FormatType> format_types;

    // Start dragging " worl".
    textfield_->SelectRange(gfx::Range(5, 10));
    gfx::Point point(GetCursorPositionX(7), 0);
    ui::MouseEvent click_a(ui::ET_MOUSE_PRESSED, point, point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(click_a);
    EXPECT_TRUE(textfield_->CanStartDragForView(textfield_, click_a.location(),
        gfx::Point()));
    operations = textfield_->GetDragOperationsForView(textfield_,
        click_a.location());
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE | ui::DragDropTypes::DRAG_COPY,
        operations);
    textfield_->WriteDragDataForView(NULL, click_a.location(), &data);
    EXPECT_TRUE(data.GetString(&string));
    EXPECT_EQ(textfield_->GetSelectedText(), string);
    EXPECT_TRUE(textfield_->GetDropFormats(&formats, &format_types));
    EXPECT_EQ(ui::OSExchangeData::STRING, formats);
    EXPECT_TRUE(format_types.empty());

    // Drop " worl" after "h".
    EXPECT_TRUE(textfield_->CanDrop(data));
    gfx::Point drop_point(GetCursorPositionX(1), 0);
    ui::DropTargetEvent drop_a(data, drop_point, drop_point, operations);
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnDragUpdated(drop_a));
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnPerformDrop(drop_a));
    EXPECT_STR_EQ("h worlellod", textfield_->text());
    textfield_->OnDragDone();

    // Undo/Redo the drag&drop change.
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("hello world", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("hello world", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("h worlellod", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("h worlellod", textfield_->text());
}

TEST_F(TextfieldTest, DragAndDrop_Canceled)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));

    // Start dragging "worl".
    textfield_->SelectRange(gfx::Range(6, 10));
    gfx::Point point(GetCursorPositionX(8), 0);
    ui::MouseEvent click(ui::ET_MOUSE_PRESSED, point, point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(click);
    ui::OSExchangeData data;
    textfield_->WriteDragDataForView(NULL, click.location(), &data);
    EXPECT_TRUE(textfield_->CanDrop(data));
    // Drag the text over somewhere valid, outside the current selection.
    gfx::Point drop_point(GetCursorPositionX(2), 0);
    ui::DropTargetEvent drop(data, drop_point, drop_point,
        ui::DragDropTypes::DRAG_MOVE);
    EXPECT_EQ(ui::DragDropTypes::DRAG_MOVE, textfield_->OnDragUpdated(drop));
    // "Cancel" the drag, via move and release over the selection, and OnDragDone.
    gfx::Point drag_point(GetCursorPositionX(9), 0);
    ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, drag_point, drag_point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, drag_point, drag_point,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMouseDragged(drag);
    textfield_->OnMouseReleased(release);
    textfield_->OnDragDone();
    EXPECT_EQ(ASCIIToUTF16("hello world"), textfield_->text());
}

TEST_F(TextfieldTest, ReadOnlyTest)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("read only"));
    textfield_->SetReadOnly(true);
    EXPECT_TRUE(textfield_->enabled());
    EXPECT_TRUE(textfield_->IsFocusable());

    bool shift = false;
    SendHomeEvent(shift);
    EXPECT_EQ(0U, textfield_->GetCursorPosition());
    SendEndEvent(shift);
    EXPECT_EQ(9U, textfield_->GetCursorPosition());

    SendKeyEvent(ui::VKEY_LEFT, shift, false);
    EXPECT_EQ(8U, textfield_->GetCursorPosition());
    SendWordEvent(ui::VKEY_LEFT, shift);
    EXPECT_EQ(5U, textfield_->GetCursorPosition());
    shift = true;
    SendWordEvent(ui::VKEY_LEFT, shift);
    EXPECT_EQ(0U, textfield_->GetCursorPosition());
    EXPECT_STR_EQ("read ", textfield_->GetSelectedText());
    textfield_->SelectAll(false);
    EXPECT_STR_EQ("read only", textfield_->GetSelectedText());

    // Cut should be disabled.
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test");
    EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_CUT));
    textfield_->ExecuteCommand(IDS_APP_CUT, 0);
    SendKeyEvent(ui::VKEY_X, false, true);
    SendAlternateCut();
    EXPECT_STR_EQ("Test", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("read only", textfield_->text());

    // Paste should be disabled.
    EXPECT_FALSE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE));
    textfield_->ExecuteCommand(IDS_APP_PASTE, 0);
    SendKeyEvent(ui::VKEY_V, false, true);
    SendAlternatePaste();
    EXPECT_STR_EQ("read only", textfield_->text());

    // Copy should work normally.
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test");
    EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY));
    textfield_->ExecuteCommand(IDS_APP_COPY, 0);
    EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test");
    SendKeyEvent(ui::VKEY_C, false, true);
    EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "Test");
    SendAlternateCopy();
    EXPECT_STR_EQ("read only", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));

    // SetText should work even in read only mode.
    textfield_->SetText(ASCIIToUTF16(" four five six "));
    EXPECT_STR_EQ(" four five six ", textfield_->text());

    textfield_->SelectAll(false);
    EXPECT_STR_EQ(" four five six ", textfield_->GetSelectedText());

    // Text field is unmodifiable and selection shouldn't change.
    SendKeyEvent(ui::VKEY_DELETE);
    EXPECT_STR_EQ(" four five six ", textfield_->GetSelectedText());
    SendKeyEvent(ui::VKEY_BACK);
    EXPECT_STR_EQ(" four five six ", textfield_->GetSelectedText());
    SendKeyEvent(ui::VKEY_T);
    EXPECT_STR_EQ(" four five six ", textfield_->GetSelectedText());
}

TEST_F(TextfieldTest, TextInputClientTest)
{
    InitTextfield();
    ui::TextInputClient* client = textfield_;
    EXPECT_TRUE(client);
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_TEXT, client->GetTextInputType());

    textfield_->SetText(ASCIIToUTF16("0123456789"));
    gfx::Range range;
    EXPECT_TRUE(client->GetTextRange(&range));
    EXPECT_EQ(0U, range.start());
    EXPECT_EQ(10U, range.end());

    EXPECT_TRUE(client->SetSelectionRange(gfx::Range(1, 4)));
    EXPECT_TRUE(client->GetSelectionRange(&range));
    EXPECT_EQ(gfx::Range(1, 4), range);

    base::string16 substring;
    EXPECT_TRUE(client->GetTextFromRange(range, &substring));
    EXPECT_STR_EQ("123", substring);

    EXPECT_TRUE(client->DeleteRange(range));
    EXPECT_STR_EQ("0456789", textfield_->text());

    ui::CompositionText composition;
    composition.text = UTF8ToUTF16("321");
    // Set composition through input method.
    input_method_->Clear();
    input_method_->SetCompositionTextForNextKey(composition);
    textfield_->clear();

    on_before_user_action_ = on_after_user_action_ = 0;

    // Send a key to trigger MockInputMethod::DispatchKeyEvent(). Note the
    // specific VKEY isn't used (MockInputMethod will mock a ui::VKEY_PROCESSKEY
    // whenever it has a test composition). However, on Mac, it can't be a letter
    // (e.g. VKEY_A) since all native character events on Mac are unicode events
    // and don't have a meaningful ui::KeyEvent that would trigger
    // DispatchKeyEvent().
    SendKeyEvent(ui::VKEY_RETURN);

    EXPECT_TRUE(textfield_->key_received());
    EXPECT_FALSE(textfield_->key_handled());
    EXPECT_TRUE(client->HasCompositionText());
    EXPECT_TRUE(client->GetCompositionTextRange(&range));
    EXPECT_STR_EQ("0321456789", textfield_->text());
    EXPECT_EQ(gfx::Range(1, 4), range);
    EXPECT_EQ(1, on_before_user_action_);
    EXPECT_EQ(1, on_after_user_action_);

    input_method_->SetResultTextForNextKey(UTF8ToUTF16("123"));
    on_before_user_action_ = on_after_user_action_ = 0;
    textfield_->clear();
    SendKeyEvent(ui::VKEY_RETURN);
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_FALSE(textfield_->key_handled());
    EXPECT_FALSE(client->HasCompositionText());
    EXPECT_FALSE(input_method_->cancel_composition_called());
    EXPECT_STR_EQ("0123456789", textfield_->text());
    EXPECT_EQ(1, on_before_user_action_);
    EXPECT_EQ(1, on_after_user_action_);

    input_method_->Clear();
    input_method_->SetCompositionTextForNextKey(composition);
    textfield_->clear();
    SendKeyEvent(ui::VKEY_RETURN);
    EXPECT_TRUE(client->HasCompositionText());
    EXPECT_STR_EQ("0123321456789", textfield_->text());

    on_before_user_action_ = on_after_user_action_ = 0;
    textfield_->clear();
    SendKeyEvent(ui::VKEY_RIGHT);
    EXPECT_FALSE(client->HasCompositionText());
    EXPECT_TRUE(input_method_->cancel_composition_called());
    EXPECT_TRUE(textfield_->key_received());
    EXPECT_TRUE(textfield_->key_handled());
    EXPECT_STR_EQ("0123321456789", textfield_->text());
    EXPECT_EQ(8U, textfield_->GetCursorPosition());
    EXPECT_EQ(1, on_before_user_action_);
    EXPECT_EQ(1, on_after_user_action_);

    textfield_->clear();
    textfield_->SetText(ASCIIToUTF16("0123456789"));
    EXPECT_TRUE(client->SetSelectionRange(gfx::Range(5, 5)));
    client->ExtendSelectionAndDelete(4, 2);
    EXPECT_STR_EQ("0789", textfield_->text());

    // On{Before,After}UserAction should be called by whatever user action
    // triggers clearing or setting a selection if appropriate.
    on_before_user_action_ = on_after_user_action_ = 0;
    textfield_->clear();
    textfield_->ClearSelection();
    textfield_->SelectAll(false);
    EXPECT_EQ(0, on_before_user_action_);
    EXPECT_EQ(0, on_after_user_action_);

    input_method_->Clear();

    // Changing the Textfield to readonly shouldn't change the input client, since
    // it's still required for selections and clipboard copy.
    ui::TextInputClient* text_input_client = textfield_;
    EXPECT_TRUE(text_input_client);
    EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, text_input_client->GetTextInputType());
    textfield_->SetReadOnly(true);
    EXPECT_TRUE(input_method_->text_input_type_changed());
    EXPECT_EQ(ui::TEXT_INPUT_TYPE_NONE, text_input_client->GetTextInputType());

    input_method_->Clear();
    textfield_->SetReadOnly(false);
    EXPECT_TRUE(input_method_->text_input_type_changed());
    EXPECT_NE(ui::TEXT_INPUT_TYPE_NONE, text_input_client->GetTextInputType());

    input_method_->Clear();
    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
    EXPECT_TRUE(input_method_->text_input_type_changed());
}

TEST_F(TextfieldTest, UndoRedoTest)
{
    InitTextfield();
    SendKeyEvent(ui::VKEY_A);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("a", textfield_->text());

    // AppendText
    textfield_->AppendText(ASCIIToUTF16("b"));
    last_contents_.clear(); // AppendText doesn't call ContentsChanged.
    EXPECT_STR_EQ("ab", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("ab", textfield_->text());

    // SetText
    SendKeyEvent(ui::VKEY_C);
    // Undo'ing append moves the cursor to the end for now.
    // A no-op SetText won't add a new edit; see TextfieldModel::SetText.
    EXPECT_STR_EQ("abc", textfield_->text());
    textfield_->SetText(ASCIIToUTF16("abc"));
    EXPECT_STR_EQ("abc", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("ab", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("abc", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("abc", textfield_->text());
    textfield_->SetText(ASCIIToUTF16("123"));
    textfield_->SetText(ASCIIToUTF16("123"));
    EXPECT_STR_EQ("123", textfield_->text());
    SendKeyEvent(ui::VKEY_END, false, false);
    SendKeyEvent(ui::VKEY_4, false, false);
    EXPECT_STR_EQ("1234", textfield_->text());
    last_contents_.clear();
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("123", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    // the insert edit "c" and set edit "123" are merged to single edit,
    // so text becomes "ab" after undo.
    EXPECT_STR_EQ("ab", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("ab", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("123", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("1234", textfield_->text());

    // Undoing to the same text shouldn't call ContentsChanged.
    SendKeyEvent(ui::VKEY_A, false, true); // select all
    SendKeyEvent(ui::VKEY_A);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_B);
    SendKeyEvent(ui::VKEY_C);
    EXPECT_STR_EQ("abc", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("1234", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("abc", textfield_->text());

    // Delete/Backspace
    SendKeyEvent(ui::VKEY_BACK);
    EXPECT_STR_EQ("ab", textfield_->text());
    SendHomeEvent(false);
    SendKeyEvent(ui::VKEY_DELETE);
    EXPECT_STR_EQ("b", textfield_->text());
    SendKeyEvent(ui::VKEY_A, false, true);
    SendKeyEvent(ui::VKEY_DELETE);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("b", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("ab", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("abc", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("ab", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("b", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("", textfield_->text());
}

// Most platforms support Ctrl+Y as an alternative to Ctrl+Shift+Z, but on Mac
// that is bound to "Show full history", so is not mapped as an editing
// command. So, on Mac, send Cmd+Shift+Z.
#if !defined(OS_MACOSX)

// Test that Ctrl+Y works for Redo, as well as Ctrl+Shift+Z.
TEST_F(TextfieldTest, RedoWithCtrlY)
{
    InitTextfield();
    SendKeyEvent(ui::VKEY_A);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Y, false, true);
    EXPECT_STR_EQ("a", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, false, true);
    EXPECT_STR_EQ("", textfield_->text());
    SendKeyEvent(ui::VKEY_Z, true, true);
    EXPECT_STR_EQ("a", textfield_->text());
}

#endif // !defined(OS_MACOSX)

TEST_F(TextfieldTest, CutCopyPaste)
{
    InitTextfield();

    // Ensure IDS_APP_CUT cuts.
    textfield_->SetText(ASCIIToUTF16("123"));
    textfield_->SelectAll(false);
    EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_CUT));
    textfield_->ExecuteCommand(IDS_APP_CUT, 0);
    EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("", textfield_->text());
    EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard());

    // Ensure [Ctrl]+[x] cuts and [Ctrl]+[Alt][x] does nothing.
    textfield_->SetText(ASCIIToUTF16("456"));
    textfield_->SelectAll(false);
    SendKeyEvent(ui::VKEY_X, true, false, true, false);
    EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("456", textfield_->text());
    EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard());
    SendKeyEvent(ui::VKEY_X, false, true);
    EXPECT_STR_EQ("456", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("", textfield_->text());
    EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard());

    // Ensure [Shift]+[Delete] cuts.
    textfield_->SetText(ASCIIToUTF16("123"));
    textfield_->SelectAll(false);
    SendAlternateCut();
    EXPECT_STR_EQ("123", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("", textfield_->text());
    EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard());

    // Ensure IDS_APP_COPY copies.
    textfield_->SetText(ASCIIToUTF16("789"));
    textfield_->SelectAll(false);
    EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_COPY));
    textfield_->ExecuteCommand(IDS_APP_COPY, 0);
    EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard());

    // Ensure [Ctrl]+[c] copies and [Ctrl]+[Alt][c] does nothing.
    textfield_->SetText(ASCIIToUTF16("012"));
    textfield_->SelectAll(false);
    SendKeyEvent(ui::VKEY_C, true, false, true, false);
    EXPECT_STR_EQ("789", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard());
    SendKeyEvent(ui::VKEY_C, false, true);
    EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard());

    // Ensure [Ctrl]+[Insert] copies.
    textfield_->SetText(ASCIIToUTF16("345"));
    textfield_->SelectAll(false);
    SendAlternateCopy();
    EXPECT_STR_EQ("345", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("345", textfield_->text());
    EXPECT_EQ(ui::CLIPBOARD_TYPE_COPY_PASTE, GetAndResetCopiedToClipboard());

    // Ensure IDS_APP_PASTE, [Ctrl]+[V], and [Shift]+[Insert] pastes;
    // also ensure that [Ctrl]+[Alt]+[V] does nothing.
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "abc");
    textfield_->SetText(base::string16());
    EXPECT_TRUE(textfield_->IsCommandIdEnabled(IDS_APP_PASTE));
    textfield_->ExecuteCommand(IDS_APP_PASTE, 0);
    EXPECT_STR_EQ("abc", textfield_->text());
    SendKeyEvent(ui::VKEY_V, false, true);
    EXPECT_STR_EQ("abcabc", textfield_->text());
    SendAlternatePaste();
    EXPECT_STR_EQ("abcabcabc", textfield_->text());
    SendKeyEvent(ui::VKEY_V, true, false, true, false);
    EXPECT_STR_EQ("abcabcabc", textfield_->text());

    // Ensure [Ctrl]+[Shift]+[Insert] is a no-op.
    textfield_->SelectAll(false);
    SendKeyEvent(ui::VKEY_INSERT, true, true);
    EXPECT_STR_EQ("abc", GetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE));
    EXPECT_STR_EQ("abcabcabc", textfield_->text());
    EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard());
}

TEST_F(TextfieldTest, OvertypeMode)
{
    InitTextfield();
    // Overtype mode should be disabled (no-op [Insert]).
    textfield_->SetText(ASCIIToUTF16("2"));
    const bool shift = false;
    SendHomeEvent(shift);
    // Note: On Mac, there is no insert key. Insert sends kVK_Help. Currently,
    // since there is no overtype on toolkit-views, the behavior happens to match.
    // However, there's no enable-overtype equivalent key combination on OSX.
    SendKeyEvent(ui::VKEY_INSERT);
    SendKeyEvent(ui::VKEY_1, false, false);
    EXPECT_STR_EQ("12", textfield_->text());
}

TEST_F(TextfieldTest, TextCursorDisplayTest)
{
    InitTextfield();
    // LTR-RTL string in LTR context.
    SendKeyEvent('a');
    EXPECT_STR_EQ("a", textfield_->text());
    int x = GetCursorBounds().x();
    int prev_x = x;

    SendKeyEvent('b');
    EXPECT_STR_EQ("ab", textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_LT(prev_x, x);
    prev_x = x;

    SendKeyEvent(0x05E1);
    EXPECT_EQ(WideToUTF16(L"ab\x05E1"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GE(1, std::abs(x - prev_x));

    SendKeyEvent(0x05E2);
    EXPECT_EQ(WideToUTF16(L"ab\x05E1\x5E2"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GE(1, std::abs(x - prev_x));

    // Clear text.
    SendKeyEvent(ui::VKEY_A, false, true);
    SendKeyEvent(ui::VKEY_DELETE);

    // RTL-LTR string in LTR context.
    SendKeyEvent(0x05E1);
    EXPECT_EQ(WideToUTF16(L"\x05E1"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_EQ(GetDisplayRect().x(), x);
    prev_x = x;

    SendKeyEvent(0x05E2);
    EXPECT_EQ(WideToUTF16(L"\x05E1\x05E2"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GE(1, std::abs(x - prev_x));

    SendKeyEvent('a');
    EXPECT_EQ(WideToUTF16(L"\x05E1\x5E2"
                          L"a"),
        textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_LT(prev_x, x);
    prev_x = x;

    SendKeyEvent('b');
    EXPECT_EQ(WideToUTF16(L"\x05E1\x5E2"
                          L"ab"),
        textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_LT(prev_x, x);
}

TEST_F(TextfieldTest, TextCursorDisplayInRTLTest)
{
    std::string locale = base::i18n::GetConfiguredLocale();
    base::i18n::SetICUDefaultLocale("he");

    InitTextfield();
    // LTR-RTL string in RTL context.
    SendKeyEvent('a');
    EXPECT_STR_EQ("a", textfield_->text());
    int x = GetCursorBounds().x();
    EXPECT_EQ(GetDisplayRect().right() - 1, x);
    int prev_x = x;

    SendKeyEvent('b');
    EXPECT_STR_EQ("ab", textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GE(1, std::abs(x - prev_x));

    SendKeyEvent(0x05E1);
    EXPECT_EQ(WideToUTF16(L"ab\x05E1"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GT(prev_x, x);
    prev_x = x;

    SendKeyEvent(0x05E2);
    EXPECT_EQ(WideToUTF16(L"ab\x05E1\x5E2"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GT(prev_x, x);

    // Clear text.
    SendKeyEvent(ui::VKEY_A, false, true);
    SendKeyEvent(ui::VKEY_DELETE);

    // RTL-LTR string in RTL context.
    SendKeyEvent(0x05E1);
    EXPECT_EQ(WideToUTF16(L"\x05E1"), textfield_->text());
    x = GetCursorBounds().x();
    prev_x = x;

    SendKeyEvent(0x05E2);
    EXPECT_EQ(WideToUTF16(L"\x05E1\x05E2"), textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GT(prev_x, x);
    prev_x = x;

    SendKeyEvent('a');
    EXPECT_EQ(WideToUTF16(L"\x05E1\x5E2"
                          L"a"),
        textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GE(1, std::abs(x - prev_x));
    prev_x = x;

    SendKeyEvent('b');
    EXPECT_EQ(WideToUTF16(L"\x05E1\x5E2"
                          L"ab"),
        textfield_->text());
    x = GetCursorBounds().x();
    EXPECT_GE(1, std::abs(x - prev_x));

    // Reset locale.
    base::i18n::SetICUDefaultLocale(locale);
}

TEST_F(TextfieldTest, HitInsideTextAreaTest)
{
    InitTextfield();
    textfield_->SetText(WideToUTF16(L"ab\x05E1\x5E2"));
    std::vector<gfx::Rect> cursor_bounds;

    // Save each cursor bound.
    gfx::SelectionModel sel(0, gfx::CURSOR_FORWARD);
    cursor_bounds.push_back(GetCursorBounds(sel));

    sel = gfx::SelectionModel(1, gfx::CURSOR_BACKWARD);
    gfx::Rect bound = GetCursorBounds(sel);
    sel = gfx::SelectionModel(1, gfx::CURSOR_FORWARD);
    EXPECT_EQ(bound.x(), GetCursorBounds(sel).x());
    cursor_bounds.push_back(bound);

    // Check that a cursor at the end of the Latin portion of the text is at the
    // same position as a cursor placed at the end of the RTL Hebrew portion.
    sel = gfx::SelectionModel(2, gfx::CURSOR_BACKWARD);
    bound = GetCursorBounds(sel);
    sel = gfx::SelectionModel(4, gfx::CURSOR_BACKWARD);
    EXPECT_EQ(bound.x(), GetCursorBounds(sel).x());
    cursor_bounds.push_back(bound);

    sel = gfx::SelectionModel(3, gfx::CURSOR_BACKWARD);
    bound = GetCursorBounds(sel);
    sel = gfx::SelectionModel(3, gfx::CURSOR_FORWARD);
    EXPECT_EQ(bound.x(), GetCursorBounds(sel).x());
    cursor_bounds.push_back(bound);

    sel = gfx::SelectionModel(2, gfx::CURSOR_FORWARD);
    bound = GetCursorBounds(sel);
    sel = gfx::SelectionModel(4, gfx::CURSOR_FORWARD);
    EXPECT_EQ(bound.x(), GetCursorBounds(sel).x());
    cursor_bounds.push_back(bound);

    // Expected cursor position when clicking left and right of each character.
    size_t cursor_pos_expected[] = { 0, 1, 1, 2, 4, 3, 3, 2 };

    int index = 0;
    for (int i = 0; i < static_cast<int>(cursor_bounds.size() - 1); ++i) {
        int half_width = (cursor_bounds[i + 1].x() - cursor_bounds[i].x()) / 2;
        MouseClick(cursor_bounds[i], half_width / 2);
        EXPECT_EQ(cursor_pos_expected[index++], textfield_->GetCursorPosition());

        // To avoid trigger double click. Not using sleep() since it takes longer
        // for the test to run if using sleep().
        NonClientMouseClick();

        MouseClick(cursor_bounds[i + 1], -(half_width / 2));
        EXPECT_EQ(cursor_pos_expected[index++], textfield_->GetCursorPosition());

        NonClientMouseClick();
    }
}

TEST_F(TextfieldTest, HitOutsideTextAreaTest)
{
    InitTextfield();

    // LTR-RTL string in LTR context.
    textfield_->SetText(WideToUTF16(L"ab\x05E1\x5E2"));

    const bool shift = false;
    SendHomeEvent(shift);
    gfx::Rect bound = GetCursorBounds();
    MouseClick(bound, -10);
    EXPECT_EQ(bound, GetCursorBounds());

    SendEndEvent(shift);
    bound = GetCursorBounds();
    MouseClick(bound, 10);
    EXPECT_EQ(bound, GetCursorBounds());

    NonClientMouseClick();

    // RTL-LTR string in LTR context.
    textfield_->SetText(WideToUTF16(L"\x05E1\x5E2"
                                    L"ab"));

    SendHomeEvent(shift);
    bound = GetCursorBounds();
    MouseClick(bound, 10);
    EXPECT_EQ(bound, GetCursorBounds());

    SendEndEvent(shift);
    bound = GetCursorBounds();
    MouseClick(bound, -10);
    EXPECT_EQ(bound, GetCursorBounds());
}

TEST_F(TextfieldTest, HitOutsideTextAreaInRTLTest)
{
    std::string locale = base::i18n::GetConfiguredLocale();
    base::i18n::SetICUDefaultLocale("he");

    InitTextfield();

    // RTL-LTR string in RTL context.
    textfield_->SetText(WideToUTF16(L"\x05E1\x5E2"
                                    L"ab"));
    const bool shift = false;
    SendHomeEvent(shift);
    gfx::Rect bound = GetCursorBounds();
    MouseClick(bound, 10);
    EXPECT_EQ(bound, GetCursorBounds());

    SendEndEvent(shift);
    bound = GetCursorBounds();
    MouseClick(bound, -10);
    EXPECT_EQ(bound, GetCursorBounds());

    NonClientMouseClick();

    // LTR-RTL string in RTL context.
    textfield_->SetText(WideToUTF16(L"ab\x05E1\x5E2"));
    SendHomeEvent(shift);
    bound = GetCursorBounds();
    MouseClick(bound, -10);
    EXPECT_EQ(bound, GetCursorBounds());

    SendEndEvent(shift);
    bound = GetCursorBounds();
    MouseClick(bound, 10);
    EXPECT_EQ(bound, GetCursorBounds());

    // Reset locale.
    base::i18n::SetICUDefaultLocale(locale);
}

TEST_F(TextfieldTest, OverflowTest)
{
    InitTextfield();

    base::string16 str;
    for (int i = 0; i < 500; ++i)
        SendKeyEvent('a');
    SendKeyEvent(kHebrewLetterSamekh);
    EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));

    // Test mouse pointing.
    MouseClick(GetCursorBounds(), -1);
    EXPECT_EQ(500U, textfield_->GetCursorPosition());

    // Clear text.
    SendKeyEvent(ui::VKEY_A, false, true);
    SendKeyEvent(ui::VKEY_DELETE);

    for (int i = 0; i < 500; ++i)
        SendKeyEvent(kHebrewLetterSamekh);
    SendKeyEvent('a');
    EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));

    MouseClick(GetCursorBounds(), -1);
    EXPECT_EQ(501U, textfield_->GetCursorPosition());
}

TEST_F(TextfieldTest, OverflowInRTLTest)
{
    std::string locale = base::i18n::GetConfiguredLocale();
    base::i18n::SetICUDefaultLocale("he");

    InitTextfield();

    base::string16 str;
    for (int i = 0; i < 500; ++i)
        SendKeyEvent('a');
    SendKeyEvent(kHebrewLetterSamekh);
    EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));

    MouseClick(GetCursorBounds(), 1);
    EXPECT_EQ(501U, textfield_->GetCursorPosition());

    // Clear text.
    SendKeyEvent(ui::VKEY_A, false, true);
    SendKeyEvent(ui::VKEY_DELETE);

    for (int i = 0; i < 500; ++i)
        SendKeyEvent(kHebrewLetterSamekh);
    SendKeyEvent('a');
    EXPECT_TRUE(GetDisplayRect().Contains(GetCursorBounds()));

    MouseClick(GetCursorBounds(), 1);
    EXPECT_EQ(500U, textfield_->GetCursorPosition());

    // Reset locale.
    base::i18n::SetICUDefaultLocale(locale);
}

TEST_F(TextfieldTest, GetCompositionCharacterBoundsTest)
{
    InitTextfield();
    ui::CompositionText composition;
    composition.text = UTF8ToUTF16("abc123");
    const uint32_t char_count = static_cast<uint32_t>(composition.text.length());
    ui::TextInputClient* client = textfield_;

    // Compare the composition character bounds with surrounding cursor bounds.
    for (uint32_t i = 0; i < char_count; ++i) {
        composition.selection = gfx::Range(i);
        client->SetCompositionText(composition);
        gfx::Point cursor_origin = GetCursorBounds().origin();
        views::View::ConvertPointToScreen(textfield_, &cursor_origin);

        composition.selection = gfx::Range(i + 1);
        client->SetCompositionText(composition);
        gfx::Point next_cursor_bottom_left = GetCursorBounds().bottom_left();
        views::View::ConvertPointToScreen(textfield_, &next_cursor_bottom_left);

        gfx::Rect character;
        EXPECT_TRUE(client->GetCompositionCharacterBounds(i, &character));
        EXPECT_EQ(character.origin(), cursor_origin) << " i=" << i;
        EXPECT_EQ(character.bottom_right(), next_cursor_bottom_left) << " i=" << i;
    }

    // Return false if the index is out of range.
    gfx::Rect rect;
    EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count, &rect));
    EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count + 1, &rect));
    EXPECT_FALSE(client->GetCompositionCharacterBounds(char_count + 100, &rect));
}

TEST_F(TextfieldTest, GetCompositionCharacterBounds_ComplexText)
{
    InitTextfield();

    const base::char16 kUtf16Chars[] = {
        // U+0020 SPACE
        0x0020,
        // U+1F408 (CAT) as surrogate pair
        0xd83d,
        0xdc08,
        // U+5642 as Ideographic Variation Sequences
        0x5642,
        0xDB40,
        0xDD00,
        // U+260E (BLACK TELEPHONE) as Emoji Variation Sequences
        0x260E,
        0xFE0F,
        // U+0020 SPACE
        0x0020,
    };
    const size_t kUtf16CharsCount = arraysize(kUtf16Chars);

    ui::CompositionText composition;
    composition.text.assign(kUtf16Chars, kUtf16Chars + kUtf16CharsCount);
    ui::TextInputClient* client = textfield_;
    client->SetCompositionText(composition);

    // Make sure GetCompositionCharacterBounds never fails for index.
    gfx::Rect rects[kUtf16CharsCount];
    gfx::Rect prev_cursor = GetCursorBounds();
    for (uint32_t i = 0; i < kUtf16CharsCount; ++i)
        EXPECT_TRUE(client->GetCompositionCharacterBounds(i, &rects[i]));

    // Here we might expect the following results but it actually depends on how
    // Uniscribe or HarfBuzz treats them with given font.
    // - rects[1] == rects[2]
    // - rects[3] == rects[4] == rects[5]
    // - rects[6] == rects[7]
}

// The word we select by double clicking should remain selected regardless of
// where we drag the mouse afterwards without releasing the left button.
TEST_F(TextfieldTest, KeepInitiallySelectedWord)
{
    InitTextfield();

    textfield_->SetText(ASCIIToUTF16("abc def ghi"));

    textfield_->SelectRange(gfx::Range(5, 5));
    const gfx::Rect middle_cursor = GetCursorBounds();
    textfield_->SelectRange(gfx::Range(0, 0));
    const gfx::Point beginning = GetCursorBounds().origin();

    // Double click, but do not release the left button.
    MouseClick(middle_cursor, 0);
    const gfx::Point middle(middle_cursor.x(),
        middle_cursor.y() + middle_cursor.height() / 2);
    ui::MouseEvent press_event(ui::ET_MOUSE_PRESSED, middle, middle,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(press_event);
    EXPECT_EQ(gfx::Range(4, 7), textfield_->GetSelectedRange());

    // Drag the mouse to the beginning of the textfield.
    ui::MouseEvent drag_event(ui::ET_MOUSE_DRAGGED, beginning, beginning,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON, 0);
    textfield_->OnMouseDragged(drag_event);
    EXPECT_EQ(gfx::Range(7, 0), textfield_->GetSelectedRange());
}

#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
// flaky: http://crbug.com/396477
TEST_F(TextfieldTest, DISABLED_SelectionClipboard)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("0123"));
    gfx::Point point_1(GetCursorPositionX(1), 0);
    gfx::Point point_2(GetCursorPositionX(2), 0);
    gfx::Point point_3(GetCursorPositionX(3), 0);
    gfx::Point point_4(GetCursorPositionX(4), 0);

    // Text selected by the mouse should be placed on the selection clipboard.
    ui::MouseEvent press(ui::ET_MOUSE_PRESSED, point_1, point_1,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(press);
    ui::MouseEvent drag(ui::ET_MOUSE_DRAGGED, point_3, point_3,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMouseDragged(drag);
    ui::MouseEvent release(ui::ET_MOUSE_RELEASED, point_3, point_3,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMouseReleased(release);
    EXPECT_EQ(gfx::Range(1, 3), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("12", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));

    // Select-all should update the selection clipboard.
    SendKeyEvent(ui::VKEY_A, false, true);
    EXPECT_EQ(gfx::Range(0, 4), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());

    // Shift-click selection modifications should update the clipboard.
    NonClientMouseClick();
    ui::MouseEvent press_2(ui::ET_MOUSE_PRESSED, point_2, point_2,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    press_2.set_flags(press_2.flags() | ui::EF_SHIFT_DOWN);
#if defined(USE_X11)
    ui::UpdateX11EventForFlags(&press_2);
#endif
    textfield_->OnMousePressed(press_2);
    ui::MouseEvent release_2(ui::ET_MOUSE_RELEASED, point_2, point_2,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMouseReleased(release_2);
    EXPECT_EQ(gfx::Range(0, 2), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());

    // Shift-Left/Right should update the selection clipboard.
    SendKeyEvent(ui::VKEY_RIGHT, true, false);
    EXPECT_STR_EQ("012", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());
    SendKeyEvent(ui::VKEY_LEFT, true, false);
    EXPECT_STR_EQ("01", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());
    SendKeyEvent(ui::VKEY_RIGHT, true, true);
    EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());

    // Moving the cursor without a selection should not change the clipboard.
    SendKeyEvent(ui::VKEY_LEFT, false, false);
    EXPECT_EQ(gfx::Range(0, 0), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard());

    // Middle clicking should paste at the mouse (not cursor) location.
    // The cursor should be placed at the end of the pasted text.
    ui::MouseEvent middle(ui::ET_MOUSE_PRESSED, point_4, point_4,
        ui::EventTimeForNow(), ui::EF_MIDDLE_MOUSE_BUTTON,
        ui::EF_MIDDLE_MOUSE_BUTTON);
    textfield_->OnMousePressed(middle);
    EXPECT_STR_EQ("01230123", textfield_->text());
    EXPECT_EQ(gfx::Range(8, 8), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));

    // Middle clicking on an unfocused textfield should focus it and paste.
    textfield_->GetFocusManager()->ClearFocus();
    EXPECT_FALSE(textfield_->HasFocus());
    textfield_->OnMousePressed(middle);
    EXPECT_TRUE(textfield_->HasFocus());
    EXPECT_STR_EQ("012301230123", textfield_->text());
    EXPECT_EQ(gfx::Range(8, 8), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("0123", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));

    // Middle clicking with an empty selection clipboard should still focus.
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, std::string());
    textfield_->GetFocusManager()->ClearFocus();
    EXPECT_FALSE(textfield_->HasFocus());
    textfield_->OnMousePressed(middle);
    EXPECT_TRUE(textfield_->HasFocus());
    EXPECT_STR_EQ("012301230123", textfield_->text());
    EXPECT_EQ(gfx::Range(4, 4), textfield_->GetSelectedRange());
    EXPECT_TRUE(GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION).empty());

    // Middle clicking in the selection should clear the clipboard and selection.
    SetClipboardText(ui::CLIPBOARD_TYPE_COPY_PASTE, "foo");
    textfield_->SelectRange(gfx::Range(2, 6));
    textfield_->OnMousePressed(middle);
    EXPECT_STR_EQ("012301230123", textfield_->text());
    EXPECT_EQ(gfx::Range(6, 6), textfield_->GetSelectedRange());
    EXPECT_TRUE(GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION).empty());

    // Double and triple clicking should update the clipboard contents.
    textfield_->SetText(ASCIIToUTF16("ab cd ef"));
    gfx::Point word(GetCursorPositionX(4), 0);
    ui::MouseEvent press_word(ui::ET_MOUSE_PRESSED, word, word,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(press_word);
    ui::MouseEvent release_word(ui::ET_MOUSE_RELEASED, word, word,
        ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMouseReleased(release_word);
    ui::MouseEvent double_click(ui::ET_MOUSE_PRESSED, word, word,
        ui::EventTimeForNow(),
        ui::EF_LEFT_MOUSE_BUTTON | ui::EF_IS_DOUBLE_CLICK,
        ui::EF_LEFT_MOUSE_BUTTON);
    textfield_->OnMousePressed(double_click);
    textfield_->OnMouseReleased(release_word);
    EXPECT_EQ(gfx::Range(3, 5), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("cd", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());
    textfield_->OnMousePressed(press_word);
    textfield_->OnMouseReleased(release_word);
    EXPECT_EQ(gfx::Range(0, 8), textfield_->GetSelectedRange());
    EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_SELECTION, GetAndResetCopiedToClipboard());

    // Selecting a range of text without any user interaction should not change
    // the clipboard content.
    textfield_->SelectRange(gfx::Range(0, 3));
    EXPECT_STR_EQ("ab ", textfield_->GetSelectedText());
    EXPECT_STR_EQ("ab cd ef", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard());

    SetClipboardText(ui::CLIPBOARD_TYPE_SELECTION, "other");
    textfield_->SelectAll(false);
    EXPECT_STR_EQ("other", GetClipboardText(ui::CLIPBOARD_TYPE_SELECTION));
    EXPECT_EQ(ui::CLIPBOARD_TYPE_LAST, GetAndResetCopiedToClipboard());
}
#endif

// Long_Press gesture in Textfield can initiate a drag and drop now.
TEST_F(TextfieldTest, TestLongPressInitiatesDragDrop)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("Hello string world"));

    // Ensure the textfield will provide selected text for drag data.
    textfield_->SelectRange(gfx::Range(6, 12));
    const gfx::Point kStringPoint(GetCursorPositionX(9), 0);

    // Enable touch-drag-drop to make long press effective.
    base::CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kEnableTouchDragDrop);

    // Create a long press event in the selected region should start a drag.
    GestureEventForTest long_press(
        kStringPoint.x(),
        kStringPoint.y(),
        ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    textfield_->OnGestureEvent(&long_press);
    EXPECT_TRUE(textfield_->CanStartDragForView(NULL, kStringPoint,
        kStringPoint));
}

TEST_F(TextfieldTest, GetTextfieldBaseline_FontFallbackTest)
{
    InitTextfield();
    textfield_->SetText(UTF8ToUTF16("abc"));
    const int old_baseline = textfield_->GetBaseline();

    // Set text which may fall back to a font which has taller baseline than
    // the default font.
    textfield_->SetText(UTF8ToUTF16("\xE0\xB9\x91"));
    const int new_baseline = textfield_->GetBaseline();

    // Regardless of the text, the baseline must be the same.
    EXPECT_EQ(new_baseline, old_baseline);
}

// Tests that a textfield view can be destroyed from OnKeyEvent() on its
// controller and it does not crash.
TEST_F(TextfieldTest, DestroyingTextfieldFromOnKeyEvent)
{
    InitTextfield();

    // The controller assumes ownership of the textfield.
    TextfieldDestroyerController controller(textfield_);
    EXPECT_TRUE(controller.target());

    // Send a key to trigger OnKeyEvent().
    SendKeyEvent(ui::VKEY_RETURN);

    EXPECT_FALSE(controller.target());
}

class TextfieldTouchSelectionTest : public TextfieldTest {
protected:
    // Simulates a complete tap.
    void Tap(const gfx::Point& point)
    {
        GestureEventForTest begin(
            point.x(), point.y(), ui::GestureEventDetails(ui::ET_GESTURE_BEGIN));
        textfield_->OnGestureEvent(&begin);

        GestureEventForTest tap_down(
            point.x(), point.y(), ui::GestureEventDetails(ui::ET_GESTURE_TAP_DOWN));
        textfield_->OnGestureEvent(&tap_down);

        GestureEventForTest show_press(
            point.x(),
            point.y(),
            ui::GestureEventDetails(ui::ET_GESTURE_SHOW_PRESS));
        textfield_->OnGestureEvent(&show_press);

        ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP);
        tap_details.set_tap_count(1);
        GestureEventForTest tap(point.x(), point.y(), tap_details);
        textfield_->OnGestureEvent(&tap);

        GestureEventForTest end(
            point.x(), point.y(), ui::GestureEventDetails(ui::ET_GESTURE_END));
        textfield_->OnGestureEvent(&end);
    }
};

// Touch selection and dragging currently only works for chromeos.
#if defined(OS_CHROMEOS)
TEST_F(TextfieldTouchSelectionTest, TouchSelectionAndDraggingTest)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));
    EXPECT_FALSE(test_api_->touch_selection_controller());
    const int x = GetCursorPositionX(2);

    // Tapping on the textfield should turn on the TouchSelectionController.
    ui::GestureEventDetails tap_details(ui::ET_GESTURE_TAP);
    tap_details.set_tap_count(1);
    GestureEventForTest tap(x, 0, tap_details);
    textfield_->OnGestureEvent(&tap);
    EXPECT_TRUE(test_api_->touch_selection_controller());

    // Un-focusing the textfield should reset the TouchSelectionController
    textfield_->GetFocusManager()->ClearFocus();
    EXPECT_FALSE(test_api_->touch_selection_controller());
    textfield_->RequestFocus();

    // With touch editing enabled, long press should not show context menu.
    // Instead, select word and invoke TouchSelectionController.
    GestureEventForTest long_press_1(
        x, 0, ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    textfield_->OnGestureEvent(&long_press_1);
    EXPECT_STR_EQ("hello", textfield_->GetSelectedText());
    EXPECT_TRUE(test_api_->touch_selection_controller());
    EXPECT_TRUE(long_press_1.handled());

    // With touch drag drop enabled, long pressing in the selected region should
    // start a drag and remove TouchSelectionController.
    ASSERT_TRUE(switches::IsTouchDragDropEnabled());
    GestureEventForTest long_press_2(
        x, 0, ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    textfield_->OnGestureEvent(&long_press_2);
    EXPECT_STR_EQ("hello", textfield_->GetSelectedText());
    EXPECT_FALSE(test_api_->touch_selection_controller());
    EXPECT_FALSE(long_press_2.handled());

    // After disabling touch drag drop, long pressing again in the selection
    // region should not do anything.
    base::CommandLine::ForCurrentProcess()->AppendSwitch(
        switches::kDisableTouchDragDrop);
    ASSERT_FALSE(switches::IsTouchDragDropEnabled());
    GestureEventForTest long_press_3(
        x, 0, ui::GestureEventDetails(ui::ET_GESTURE_LONG_PRESS));
    textfield_->OnGestureEvent(&long_press_3);
    EXPECT_STR_EQ("hello", textfield_->GetSelectedText());
    EXPECT_FALSE(test_api_->touch_selection_controller());
    EXPECT_FALSE(long_press_3.handled());
}
#endif

TEST_F(TextfieldTouchSelectionTest, TouchSelectionInUnfocusableTextfield)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));
    gfx::Point touch_point(GetCursorPositionX(2), 0);

    // Disable textfield and tap on it. Touch text selection should not get
    // activated.
    textfield_->SetEnabled(false);
    Tap(touch_point);
    EXPECT_FALSE(test_api_->touch_selection_controller());
    textfield_->SetEnabled(true);

    // Make textfield unfocusable and tap on it. Touch text selection should not
    // get activated.
    textfield_->SetFocusable(false);
    Tap(touch_point);
    EXPECT_FALSE(textfield_->HasFocus());
    EXPECT_FALSE(test_api_->touch_selection_controller());
    textfield_->SetFocusable(true);
}

// No touch on desktop Mac. Tracked in http://crbug.com/445520.
#if defined(OS_MACOSX) && !defined(USE_AURA)
#define MAYBE_TapOnSelection DISABLED_TapOnSelection
#else
#define MAYBE_TapOnSelection TapOnSelection
#endif

TEST_F(TextfieldTouchSelectionTest, MAYBE_TapOnSelection)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("hello world"));
    gfx::Range sel_range(2, 7);
    gfx::Range tap_range(5, 5);
    gfx::Rect tap_rect = GetCursorBounds(gfx::SelectionModel(tap_range, gfx::CURSOR_FORWARD));
    gfx::Point tap_point = tap_rect.CenterPoint();

    // Select range |sel_range| and check if touch selection handles are not
    // present and correct range is selected.
    textfield_->SetSelectionRange(sel_range);
    gfx::Range range;
    textfield_->GetSelectionRange(&range);
    EXPECT_FALSE(test_api_->touch_selection_controller());
    EXPECT_EQ(sel_range, range);

    // Tap on selection and check if touch selectoin handles are shown, but
    // selection range is not modified.
    Tap(tap_point);
    textfield_->GetSelectionRange(&range);
    EXPECT_TRUE(test_api_->touch_selection_controller());
    EXPECT_EQ(sel_range, range);

    // Tap again on selection and check if touch selection handles are still
    // present and selection is changed to a cursor at tap location.
    Tap(tap_point);
    textfield_->GetSelectionRange(&range);
    EXPECT_TRUE(test_api_->touch_selection_controller());
    EXPECT_EQ(tap_range, range);
}

TEST_F(TextfieldTest, AccessiblePasswordTest)
{
    InitTextfield();
    textfield_->SetText(ASCIIToUTF16("password"));

    ui::AXViewState state_regular;
    textfield_->GetAccessibleState(&state_regular);
    EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, state_regular.role);
    EXPECT_EQ(ASCIIToUTF16("password"), state_regular.value);
    EXPECT_FALSE(state_regular.HasStateFlag(ui::AX_STATE_PROTECTED));

    textfield_->SetTextInputType(ui::TEXT_INPUT_TYPE_PASSWORD);
    ui::AXViewState state_protected;
    textfield_->GetAccessibleState(&state_protected);
    EXPECT_EQ(ui::AX_ROLE_TEXT_FIELD, state_protected.role);
    EXPECT_EQ(ASCIIToUTF16("********"), state_protected.value);
    EXPECT_TRUE(state_protected.HasStateFlag(ui::AX_STATE_PROTECTED));
}

} // namespace views
